/* a dummy client that keeps the server busy
 * we run a few of these in parallel to see we don't get deadlocks on the protected stack
 * and to see that the server can handle more than one client
 * we used a fixed port - 3490 for all the test
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define MAX_MSG_LEN 256

int checkAnswer(char *buffer);
int sendall(int s, char *buf, int *len);
void randMutateStr(char *str);
void randCmd(char *buffer, char *str);

int main(void){
	int count = 0; //count number of iterations

	int sockfd;     // listening socket descriptor
	char buf[MAX_MSG_LEN], cmdBuf[MAX_MSG_LEN];

	fd_set master, readfds;
	int maxfd;

	int yes=1;        // for setsockopt() SO_REUSEADDR, below
	int rv, numbytes;

	struct addrinfo hints, *ai;

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	if ((rv = getaddrinfo("127.0.0.1", "3490", &hints, &ai)) != 0) {
		fprintf(stderr, "ERROR: %s\n", gai_strerror(rv));   //rv is just for error ref.
		return 0;
	}

	sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if (sockfd < 0){
		fprintf(stdout, "ERROR: couldn't get a socket from the OS!!\n");
		freeaddrinfo(ai);
		return 0;
	}

	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

	if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) == -1) {
		close(sockfd);
		freeaddrinfo(ai);
		fprintf(stdout, "failed to connect to server\n");
		return 0;
	}

	freeaddrinfo(ai); // all done with this

	FD_ZERO(&master);
	FD_SET(sockfd, &master);
	maxfd = sockfd;
	FD_ZERO(&readfds);

	printf("you are now connected\n\n");

	srand(2);
	memset(&buf, 0, MAX_MSG_LEN);
	memset(&cmdBuf, 0, MAX_MSG_LEN);
	strcpy(buf, "qwerttyuioo");
	numbytes = strlen("qwerttyuioo")+1;

	while(1){
		if (count == 100000) break;	//give each dummy only 100 commands to pass
		printf("test command number: %d\n", count);
	//	sleep(rand() % 2);		//just to make things more interesting - delay the iterations
		randMutateStr(buf);		//change the base string
		randCmd(cmdBuf, buf);	//randomly create a command to pass
		printf(">>");
		printf("%s\n", cmdBuf);
		numbytes = strlen(cmdBuf);

		if (sendall(sockfd, cmdBuf, &numbytes) == -1){
			fprintf(stdout, "ERROR: failed to send the whole buffer\n"
					"sent only %d bytes out of %d, \n", numbytes, (int)strlen(cmdBuf));
		}

		if (strcmp("exit", cmdBuf) == 0){
			break;
		}

		readfds = master;
		if (select(maxfd+1, &readfds, NULL, NULL, NULL) == -1){
			perror("select");
			return 0;
		}

		if (FD_ISSET(sockfd, &readfds)){
			numbytes = recv(sockfd, cmdBuf, MAX_MSG_LEN ,0);
			if (numbytes == 0){    //connection closed
				break;
			}

			if (numbytes == -1){
				fprintf(stdout, "ERROR: error handling client\n");
				break;
			}
			else {
				cmdBuf[numbytes] = '\0';
				if (checkAnswer(cmdBuf)){   //check if we got an output on a top command
					printf("%s\n", cmdBuf);
				}
				if (numbytes > 23){
					printf("BUG BUG BUG BUG BUG BUG BUG \n");
					sleep(4);
				}
			}
		}
		count++;
	}

	printf("exiting stackClient\n");
	close(sockfd);
	return 1;
}


/*beej's function: wrapping a send() function to make sure big buffers
 * are sent properly
 */
int sendall(int s, char *buf, int *len)
{
	int total = 0;        // how many bytes we've sent
	int bytesleft = *len; // how many we have left to send
	int n;

	while(total < *len) {
		n = send(s, buf+total, bytesleft, 0);
		if (n == -1) { break; }
		total += n;
		bytesleft -= n;
	}

	*len = total; // return number actually sent here

	return n==-1?-1:0; // return -1 on failure, 0 on success
}

void randMutateStr(char *str){
	char *p;
	int len, i;
	
	len = (rand() % 50) + 2;
	for (p = str, i = 0; i < len; p++, i++){
		*p = ((*p + (rand() % 26)) % 26) + 'a';
	}
	str[len] = '\0';
	return;
}

void randCmd(char *buffer, char *str)
{

	int cmd = rand() % 6;

	if (cmd == 0){	//write a push command to the buffer
		strcpy(buffer, "PUSH ");
		buffer[strlen("PUSH ")] = '\0';
		strcat(buffer, str);
		buffer[ strlen(str) + strlen("PUSH ") ] = '\0';
	}
	else if (cmd == 1){
		strcpy(buffer, "POP");	//write a pop command to the buffer
		buffer[3] = '\0';
	}
	else if (cmd == 2) {
		strcpy(buffer, "TOP");	//write a top command to the buffer
		buffer[3] = '\0';
	}

	return;
}


int checkAnswer(char *buffer){
	char *p = NULL;

	p = buffer;
	while (*p != '\0' && *p != ' ')
		p++;

	if (*p == '\0') return 0; //no space means not an output on a top command

	if (*p == ' '){
		*p = '\0';   //mark the space char
		if (strcmp("OUTPUT:", buffer) == 0){
			*p = ' ';    //remove the mark
			return 1;
		}
		else {
			*p = ' ';    //remove the mark
			return 0;
		}
	}

	return 0;    //if we got here it can't be a PUSH command return 0;
}
